package at.ac.tuwien.swa.sparrow.server.security

import com.typesafe.scalalogging.log4j.Logging
import java.lang.reflect.Method
import org.aopalliance.intercept.{MethodInvocation, MethodInterceptor}
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.{Around, Aspect}
import org.aspectj.lang.reflect.MethodSignature
import org.springframework.core.annotation.AnnotationUtils
import org.springframework.util.ReflectionUtils

@Aspect
class SecurityAspect extends MethodInterceptor with Logging {

	def invoke(invocation: MethodInvocation): AnyRef = {
		handleMethodInvocation(invocation.getThis, invocation.getMethod, () => invocation.proceed())
	}

	@Around("execution(* at.ac.tuwien.swa.sparrow..*.*.*(..)) and @annotation(at.ac.tuwien.swa.sparrow.server.security.Secured)")
	def doSecuredOperation(pjp: ProceedingJoinPoint): AnyRef = {
		handleMethodInvocation(pjp.getTarget, pjp.getSignature.asInstanceOf[MethodSignature].getMethod, () => pjp.proceed())
	}

	def handleMethodInvocation(target: Any, method: Method, invoke: () => AnyRef): AnyRef = {
		var secured = AnnotationUtils.findAnnotation(method, classOf[Secured])
		if (secured == null) {
			val m = ReflectionUtils.findMethod(target.getClass, method.getName, method.getParameterTypes: _*)
			secured = AnnotationUtils.findAnnotation(m, classOf[Secured])
		}

		if (secured == null) {
			logger.error(s"Misconfiguration of security aspect detected (${target.getClass.getSimpleName}.${method.getName})!")
			throw new IllegalStateException()
		} else if (secured.value() == classOf[Object]) {
			return invoke()
		}

		val role = SecurityContext.getRole
		if (role == null) throw new SecurityException("Not authorized: no role defined in security context")

		if (secured.value isInstance role) invoke()
		else throw new SecurityException(s"Not authorized: Required role ${secured.value}, provided: ${role.getClass}")
	}
}
